package com.lvmama.java.rhino.spark.utils;

import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.script.ScriptService;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilder;

import com.alibaba.fastjson.JSONObject;

/**
 * @Title: ElasticUtils.java
 * @Package com.lvtu.monitor.utils
 * @author dengcheng
 * @date 2015-5-15 下午1:41:51
 * @version V1.0.0
 */
public class ElasticUtils {
	private static volatile ElasticUtils instance;
	private TransportClient client;
	public static final String HIGHLIGHTER_PRE_TAGS = "<span style=\"color:red;\">";
	public static final String HIGHLIGHTER_POST_TAGS = "</span>";
	
	public ElasticUtils() {
		init();
	}

	public void init() {
		// 配置你的es,现在这里只配置了集群的名,默认是elasticsearch,跟服务器的相同
		// 这里可以同时连接集群的服务器,可以多个,并且连接服务是可访问的
		String elasticClusterName = Constants.getInstance().getValue("elastic.cluster.name");
		Settings settings = Settings.settingsBuilder().put("cluster.name", elasticClusterName).build();
		client = TransportClient.builder().settings(settings).build();

		String[] elasticUrls = Constants.getInstance().getValue("elastic.url").split(",");
		for (String elasticUrl : elasticUrls) {
			String elasticHost = elasticUrl.split(":")[0];
			int elasticPort = Integer.valueOf(elasticUrl.split(":")[1]);
			try {
				client.addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName(elasticHost), elasticPort));
			} catch (UnknownHostException e) {
				e.printStackTrace();
			}
		}
	}
	
	public class ElasticSearchResult<T> implements Serializable {
		private static final long serialVersionUID = -4434715473675532823L;
		private T data;
		private String[] highlightTexts;
		public T getData() {
			return data;
		}
		public void setData(T data) {
			this.data = data;
		}
		public String[] getHighlightTexts() {
			return highlightTexts;
		}
		public void setHighlightTexts(String[] highlightTexts) {
			this.highlightTexts = highlightTexts;
		}
	}
	
	public class ElasticPageResult<T> {
		private int page;
		private int pageSize;
		private long totalSize;
		private List<T> list;
		public int getPage() {
			return page;
		}
		public void setPage(int page) {
			this.page = page;
		}
		public int getPageSize() {
			return pageSize;
		}
		public void setPageSize(int pageSize) {
			this.pageSize = pageSize;
		}
		public long getTotalSize() {
			return totalSize;
		}
		public void setTotalSize(long totalSize) {
			this.totalSize = totalSize;
		}
		public List<T> getList() {
			return list;
		}
		public void setList(List<T> list) {
			this.list = list;
		}
	}

	public static ElasticUtils getInstance() {
		if (instance == null) {
			synchronized (ElasticUtils.class) {
				if (instance == null) {
					instance = new ElasticUtils();
				}
			}
		}
		return instance;
	}

	/**
	 * 以json方式提交索引
	 * @param index		索引库名称
	 * @param type		索引类型
	 * @param json		json格式数据
	 * @throws InterruptedException
	 * @throws ExecutionException
	 * @author dengcheng
	 * @date 2015-5-15 下午1:59:54
	 */
	public void putIndex(String index, String type, String json) throws InterruptedException, ExecutionException {
		UUIDGenerator gen = new UUIDGenerator();
		putIndex(index, type, gen.generate().toString(), json);
	}
	
	/**
	 * 以json方式提交索引
	 * @param index		索引库名称
	 * @param type		索引类型
	 * @param id 		索引文档ID
	 * @param json		json格式数据
	 * @throws InterruptedException
	 * @throws ExecutionException
	 * @author dengcheng
	 * @date 2015-5-15 下午1:59:54
	 */
	public void putIndex(String index, String type, String id, String json) throws InterruptedException, ExecutionException {
		IndexRequest indexRequest = new IndexRequest(index, type, id).source(json);
		client.index(indexRequest).get();
	}
	
	/**
	 * 统计匹配的索引个数
	 * @param index		索引库名称
	 * @param query		查询条件
	 * @param filter	筛选条件
	 * @return
	 */
	public <T> Long countIndex(String index, QueryBuilder query, QueryBuilder filter) {
		SearchResponse response = client.prepareSearch(index).setFrom(0)
				.setQuery(query).setPostFilter(filter)
				.setSearchType(SearchType.COUNT).execute().actionGet();
		long hitNum = response.getHits().getTotalHits();
		return hitNum;
	}
	
	public <T> ElasticPageResult<ElasticSearchResult<T>> queryIndexAndHighlighted(String index, Class<T> clazz, QueryBuilder query, QueryBuilder filter, SortBuilder sort, int start, int size, String[] highlighteds) {
		SearchResponse response = query(index, query, filter, sort, start, size, highlighteds);
		
		ElasticPageResult<ElasticSearchResult<T>> result = new ElasticPageResult<ElasticSearchResult<T>>();
		List<ElasticSearchResult<T>> resultList = new ArrayList<ElasticSearchResult<T>>();
		result.setList(resultList);

		for (SearchHit hit : response.getHits().getHits()) {
			ElasticSearchResult<T> tempObj = new ElasticSearchResult<T>();
			resultList.add(tempObj);
			
			String value = hit.getSourceAsString();
			T data = JSONObject.parseObject(value, clazz);
			tempObj.setData(data);
			// 获取对应的高亮域
			Map<String, HighlightField> highLightResult = hit.highlightFields();
			if(highlighteds == null) {
				continue;
			}
			for(String highlighted : highlighteds) {
				// 从设定的高亮域中取得指定域
				HighlightField highlightField = highLightResult.get(highlighted);
				if(highlightField == null) {
					continue;
				}
				// 取得定义的高亮标签
				Text[] highlightTexts = highlightField.fragments();
				String[] highlight = new String[highlightTexts.length];
				for (int i = 0; i < highlightTexts.length; i++) {
					highlight[i] = highlightTexts[i].toString();
				}
				tempObj.setHighlightTexts(highlight);
			}
		}
		
		result.setTotalSize(response.getHits().getTotalHits());
		return result;
	}
	
	/**
	 * 批量获取索引值
	 * @param index		索引库名称
	 * @param clazz		匹配的对象
	 * @param query		查询条件
	 * @param filter	筛选条件
	 * @param sort		排序条件
	 * @param start		开始位置
	 * @param size		获取条数
	 * @return
	 */
	public <T> ElasticPageResult<T> queryIndex(String index, Class<T> clazz, QueryBuilder query, QueryBuilder filter, SortBuilder sort, int start, int size) {
		ElasticPageResult<ElasticSearchResult<T>> queryResult = queryIndexAndHighlighted(index, clazz, query, filter, sort, start, size, null);
		List<ElasticSearchResult<T>> resultList = queryResult.getList();
		List<T> list = new ArrayList<T>();
		for(ElasticSearchResult<T> temp : resultList) {
			list.add(temp.getData());
		}
		ElasticPageResult<T> result = new ElasticPageResult<T>();
		result.setList(list);
		result.setPageSize(queryResult.getPageSize());
		return result;
	}
	
	private SearchResponse query(String index, QueryBuilder query, QueryBuilder filter, SortBuilder sort, int start, int size, String[] highlighteds) {
		SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index).setFrom(start).setQuery(query).setPostFilter(filter).setSize(size).setExplain(true).addSort(sort);
		if(highlighteds != null && highlighteds.length > 0) {
			for (String highlighted : highlighteds) {
				searchRequestBuilder.addHighlightedField(highlighted);
			}
			searchRequestBuilder.setHighlighterPreTags(HIGHLIGHTER_PRE_TAGS);
			searchRequestBuilder.setHighlighterPostTags(HIGHLIGHTER_POST_TAGS);
		}
		SearchResponse response = searchRequestBuilder.execute().actionGet();
		return response;
	}
	
	/**
	 * 
	* @Description: TODO 
	* @author dengcheng 
	* @date 2015-6-11 上午11:40:01
	* @param params 查询参数
	* @param tpName 定义的模板名称
	* @param clazz 返回对象clazz 
	* @param sources 模板源码
	 */
	public <T> List<T> queryByTemplate(Map<String,Object> params,String tpName,Class<T> clazz,String sources){
		client.preparePutIndexedScript("mustache", tpName,sources).get();
		SearchResponse response = client.prepareSearch()
		        .setTemplateName(tpName)
		        .setTemplateType(ScriptService.ScriptType.INDEXED)
		        .setTemplateParams(params).setFrom(0).setSize(100).setExplain(true)
		        .get();
		
		List<T> exlist = new ArrayList<T>();
		for (SearchHit hit : response.getHits().getHits()) {
			String value = hit.getSourceAsString();
			exlist.add(JSONObject.parseObject(value, clazz));
		}
		return exlist;
	}

}
